#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "hi_io.h"
#include "hi_time.h"
#include "hi_adc.h"
#include "iot_errno.h"

#include "hi_wifi_api.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "lwip/netif.h"
#include "lwip/ip4_addr.h"
#include "lwip/api_shell.h"
#include "wifi_hotspot.h"

#include "wifi_device.h"
#include "wifi_device_config.h"

#include <hi_types_base.h>
#include <hi_i2c.h>
#include <hi_early_debug.h>
#include <hi_stdlib.h>


static char ssid[] ="hh";                       //wifi名称
static char password[] = "13267897941";         //wifi密码
static unsigned short port = 20222;             //socket端口设置

// 接收、发送的数据
static char request[256] = "";

// 未连接热点=0，已连接热点=1
static int g_connected = 0;


int connfd = -1;

// 输出连接信息字符串
// 打印内容样例--> bssid: 38:47:BC:49:01:FA, rssi: 0, connState: 0, reason: 0, ssid: MyMobile
void PrintLinkedInfo(WifiLinkedInfo* info) {
    if (!info) return;
    static char macAddress[32] = {0};
    unsigned char* mac = info->bssid;
    snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    printf("bssid: %s, rssi: %d, connState: %d, reason: %d, ssid: %srn", macAddress, info->rssi, info->connState, info->disconnectedReason, info->ssid);
}

// 连接状态改变事件处理
void OnWifiConnectionChanged(int state, WifiLinkedInfo* info) {
    if (!info) return;

    // 输出类似内容：OnWifiConnectionChanged 31, state = 1, info =
    printf("%s %d, state = %d, info = rn", __FUNCTION__, __LINE__, state);
    PrintLinkedInfo(info);

    // 根据连接状态设置g_connected
    if (state == WIFI_STATE_AVALIABLE) {
        g_connected = 1;
    } else {
        g_connected = 0;
    }
}

// 扫描状态改变事件处理
void OnWifiScanStateChanged(int state, int size) {
    printf("%s %d, state = %X, size = %drn", __FUNCTION__, __LINE__, state, size);
}

void DisconnectTcpSocket(int connfd) {
    sleep(1);
    printf("do_disconnect...rn");
    lwip_close(connfd);
    sleep(1); // for debug
}

void CloseTcpSocket(int socketfd) {
    printf("do_cleanup...rn");

    lwip_close(socketfd);
}

static void TcpServerHandler(void) {

    ssize_t retval = 0;
    // 创建一个通信的Socket，并返回一个Socket文件描述符。第一个参数IpV4，第二个参数SOCK_STREAM类型，第三个指用到的协议
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    // 客户端地址和地址长度
    struct sockaddr_in clientAddr = {0};
    socklen_t clientAddrLen = sizeof(clientAddr);

    // 服务端地址
    struct sockaddr_in serverAddr = {0};
    serverAddr.sin_family = AF_INET;
    // htons是将整型变量从主机字节顺序转变成网络字节顺序，就是整数在地址空间存储方式变为高位字节存放在内存的低地址处
    serverAddr.sin_port = htons(port);
    // 监听本机的所有IP地址，INADDR_ANY=0x0
    // 将主机数转换成无符号长整型的网络字节顺序
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    // 服务端绑定端口
    retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    if (retval < 0) {
        printf("bind failed, %ld!rn", retval);
        CloseTcpSocket(sockfd);
        return;
    }
    printf("bind to port %d success!rn", port);

    // 开始监听，backlog指Pending连接队列增长到的最大长度。队列满了，再有新连接请求到达，则客户端ECONNREFUSED错误。如果支持重传，则请求忽略。
    int backlog = 1;
    retval = listen(sockfd, backlog);
    if (retval < 0) {
        printf("listen failed!rn");
        CloseTcpSocket(sockfd);
        return;
    }
    printf("listen with %d backlog success!rn", backlog);

    int outerFlag = 1;
    while (outerFlag) {
        // 接受客户端连接，成功会返回一个表示连接的 socket。clientAddr参数将会携带客户端主机和端口信息；失败返回 -1
        // 从Pending连接队列中获取第一个连接，根据sockfd的socket协议、地址族等内容创建一个新的socket文件描述，并返回。
        // 此后的 收、发 都在 表示连接的 socket 上进行；之后 sockfd 依然可以继续接受其他客户端的连接，
        // UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接，父进程继续接受其他客户端的连接
        // 鸿蒙liteos-a内核之上，可以使用UNIX的“每个连接一个进程”的并发模型liteos-m内核之上，可以使用“每个连接一个线程”的并发模型
        connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);
        if (connfd < 0) {
            printf("accept failed, %d, %d\r\n", connfd, errno);
            continue;
        }
        printf("accept success, connfd = %d !\r\n", connfd);
        // inet_ntoa：网络地址转换成“.”点隔的字符串格式。ntohs：16位数由网络字节顺序转换为主机字节顺序。
        printf("client addr info: host = %s, port = %d\r\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

        int innerFlag = 1;
        // 接收消息，然后发送回去
        while (innerFlag) {
            // 后续 收、发 都在 表示连接的 socket 上进行；
            // 在新的Socket文件描述上接收信息.
            retval = recv(connfd, request, sizeof(request), 0);
            if (retval < 0) {
                printf("recv request failed, %ld!\r\n", retval);
                innerFlag = 0;
            } else if (retval == 0) {
                // 对方主动断开连接
                printf("client disconnected!\r\n");
                innerFlag = 0;
            } else {
                if (retval <= 0) {
                printf("send response failed, %ld!\r\n", retval);
                innerFlag = 0;
                }
                if(retval == 3 &&  request[0] == -86)
                {
                    printf("recv = %d %d %d \r\n",request[0],request[1],request[2]);
                    if(request[1] == 0x00){
                        retval = send(connfd, "aaok", strlen("aaok"), 0);           //答应码
                    }
                    if(request[1] == 0x01)
                    {
                    }else if(request[1] == 0x02)                                    //喂食量处理函数
                    {
                        int i = ((unsigned char)request[2]);
                        for(;i>0;i--)
                        {
                            custom(500);
                            osDelay(10);
                        }
                    }
                }
                // 清空缓冲区
                memset(&request, 0, sizeof(request));
            }
            if(innerFlag == 0)
            {
                DisconnectTcpSocket(connfd); //清除连接
                break;                       //重连
            }

        }
    }

    CloseTcpSocket(sockfd);
}

void *TcpServerTask(void *arg) {

    (void)arg;

    // 先定义两个Wifi监听器，一个连接改变、一个状态改变
    WifiEvent eventListener = {
    .OnWifiConnectionChanged = OnWifiConnectionChanged,
    .OnWifiScanStateChanged = OnWifiScanStateChanged
    };

    // 等待10个系统Ticks。每个ticks多少个us，计算方式= 1000 * 1000 / osKernelGetTickFreq()
    osDelay(10);

    // 注册监听器
    WifiErrorCode errCode = RegisterWifiEvent(&eventListener);
    printf("RegisterWifiEvent: %d\r\n", errCode);

    // 设置Wifi热点信息
    WifiDeviceConfig apConfig = {};
    strcpy(apConfig.ssid, ssid);
    strcpy(apConfig.preSharedKey, password);
    apConfig.securityType = WIFI_SEC_TYPE_PSK;

    int netId = -1;

    // 启用Wifi
    errCode = EnableWifi();
    printf("EnableWifi: %d\r\n", errCode);
    osDelay(10);

    // 设置Wifi热点配置信息，返回生成的网络Id-netId。
    errCode = AddDeviceConfig(&apConfig, &netId);
    printf("AddDeviceConfig: %d\r\n", errCode);

    // 根据网络Id连接到Wifi热点
    g_connected = 0;
    errCode = ConnectTo(netId);
    printf("ConnectTo(%d): %d\r\n", netId, errCode);
    // 未连接完成，则一直等待。g_connected状态会在事件中设置。
    while (!g_connected) {
    osDelay(10);
    }
    printf("g_connected: %d\r\n", g_connected);
    osDelay(50);

    // 联网业务开始，找到netifname=wlan0的netif。
    struct netif* iface = netifapi_netif_find("wlan0");
    if (iface) {
    // 启动DHCP客户端，获取IP地址
    err_t ret = netifapi_dhcp_start(iface);
    printf("netifapi_dhcp_start: %d\r\n", ret);
    // 等待DHCP服务器反馈给予地址
    osDelay(300);
    // 执行线程安全的网络方法，第二个参数是voidFunc，第三个参数是errFunc。如果没有errFunc，那么就执行voidFunc。
    // netifapi_dhcp_start/netifapi_dhcp_stop等都是调用的netifapi_netif_common方法。
    // dhcp_clients_info_show显示信息
    /*
    server :
        server_id : 192.168.43.1
        mask : 255.255.255.0, 1
        gw : 192.168.43.1
        T0 : 3600
        T1 : 1800
        T2 : 3150
    clients <1> :
        mac_idx mac             addr            state   lease   tries   rto
        0       b4c9b9af69f8    192.168.0.10   10      0       1       2
    */
    ret = netifapi_netif_common(iface, dhcp_clients_info_show, NULL);
    printf("netifapi_netif_common: %d\r\n", ret);
    }

    TcpServerHandler();

    // 联网业务结束，断开DHCP
    err_t ret = netifapi_dhcp_stop(iface);
    printf("netifapi_dhcp_stop: %d\r\n", ret);

    // 断开Wifi热点连接
    Disconnect();

    // 移除Wifi热点的配置
    RemoveDevice(netId);

    // 关闭Wifi
    errCode = DisableWifi();
    printf("DisableWifi: %d\r\n", errCode);
    osDelay(200);
}





void WifiSockets(void)
{

    osThreadAttr_t wifisocket;
    wifisocket.name = "TcpServerTask";
    wifisocket.attr_bits = 0U;
    wifisocket.cb_mem = NULL;
    wifisocket.cb_size = 0U;
    wifisocket.stack_mem = NULL;
    wifisocket.stack_size = 10240;
    wifisocket.priority = 25;

    if (osThreadNew(TcpServerTask, NULL, &wifisocket) == NULL) {
        printf("[Ssd1306TestDemo] Falied to create TcpServerTask!\n");
    }
}
APP_FEATURE_INIT(WifiSockets);


